{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How to optimize a forecaster"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction\n",
    "This method will traverse existing optimization methods(onnxruntime, openvino, jit, …) and save the model with minimum latency under the given data and search restrictions(accelerator, precision, accuracy_criterion) in forecaster.accelerated_model. This method is required to call before predict and evaluate. Now this function is only for non-distributed model."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Set up\n",
    "Before we begin, we need to install chronos if it isn’t already available, we choose to use pytorch as deep learning backend."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pip install --pre --upgrade bigdl-chronos[pytorch,inference]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Forecaster preparation\n",
    "\n",
    "Before the inferencing process, a forecaster should be created and trained. The training process is introduced in the previous guidance [Train forcaster on single node](https://bigdl.readthedocs.io/en/latest/doc/Chronos/Howto/how_to_train_forecaster_on_one_node.html) in detail, therefore we directly create and train a `TCNForecaster` based on the nyc taxi dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# data preparation\n",
    "def get_data():\n",
    "    from bigdl.chronos.data import get_public_dataset\n",
    "    from sklearn.preprocessing import StandardScaler\n",
    "\n",
    "    # load the nyc taxi dataset\n",
    "    tsdata_train, tsdata_val, tsdata_test = get_public_dataset(name='nyc_taxi')\n",
    "\n",
    "    stand = StandardScaler()\n",
    "    for tsdata in [tsdata_train, tsdata_val, tsdata_test]:\n",
    "        tsdata.impute()\\\n",
    "              .scale(stand, fit=tsdata is tsdata_train)\n",
    "\n",
    "    # convert `tsdata_train` and `tsdata_test` to pytorch dataloader\n",
    "    train_data = tsdata_train.to_torch_data_loader(lookback=48, horizon=1)\n",
    "    test_data = tsdata_test.to_torch_data_loader(lookback=48, horizon=1)\n",
    "\n",
    "    return train_data, test_data\n",
    "\n",
    "# trained forecaster preparation\n",
    "def get_trained_forecaster(train_data):\n",
    "    from bigdl.chronos.forecaster.tcn_forecaster import TCNForecaster\n",
    "    # create a TCNForecaster\n",
    "    forecaster = TCNForecaster(past_seq_len=48,\n",
    "                               future_seq_len=1,\n",
    "                               input_feature_num=1,\n",
    "                               output_feature_num=1)\n",
    "\n",
    "    # train the forecaster on the training data\n",
    "    forecaster.fit(train_data)\n",
    "    return forecaster"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Global seed set to 2187352814\n",
      "Global seed set to 2187352814\n",
      "GPU available: False, used: False\n",
      "TPU available: False, using: 0 TPU cores\n",
      "IPU available: False, using: 0 IPUs\n",
      "HPU available: False, using: 0 HPUs\n",
      "\n",
      "  | Name  | Type             | Params\n",
      "-------------------------------------------\n",
      "0 | model | NormalizeTSModel | 4.3 K \n",
      "1 | loss  | MSELoss          | 0     \n",
      "-------------------------------------------\n",
      "4.3 K     Trainable params\n",
      "0         Non-trainable params\n",
      "4.3 K     Total params\n",
      "0.017     Total estimated model params size (MB)\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "16035970d3b041a88477c59e45f21bba",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Training: 0it [00:00, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# get data for training and testing\n",
    "train_data, test_data = get_data()\n",
    "# get a trained forecaster\n",
    "forecaster = get_trained_forecaster(train_data)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Forecaster optimization\n",
    "\n",
    "And there are batch_size and quantize parameters you may want to change. If not familiar with manual hyperparameters tuning, just leave batch_size to the default value."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Traverse existing optimization methods(onnxruntime, openvino, jit, …) and save the model with minimum latency under the given data and search restrictions(accelerator, precision, accuracy_criterion) in forecaster.accelerated_model. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "forecaster.optimize(train_data, test_data, thread_num=1)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`forecaster.optimize` will generate an optimized model with the lowest latency.\n",
    "\n",
    "Following blocks test the prediction time for the optimized forecaster"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The optimized forecaster cost: 2.5293169021606445 s\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "\n",
    "st = time.time()\n",
    "for _ in range(100):\n",
    "    forecaster.predict(test_data)\n",
    "print(\"The optimized forecaster cost:\", time.time() - st, \"s\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Users may set `acceleration=False` to drop back to the original forecaster. It's not an usual behavior, here we use it to test the original forecaster's prediction time."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The original forecaster cost: 7.534037113189697 s\n"
     ]
    }
   ],
   "source": [
    "st = time.time()\n",
    "for _ in range(100):\n",
    "    forecaster.predict(test_data, acceleration=False)\n",
    "print(\"The original forecaster cost:\", time.time() - st, \"s\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "chronos",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.11 (default, Jul 27 2021, 14:32:16) \n[GCC 7.5.0]"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "f7cbcfcf124497a723b2fc91b0dad8cd6ed41af955928289a9d3478af9690021"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
